package com.service.alarm;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;

import com.service.bean.alarm.AlarmInfo;
import com.service.bean.alarm.AlarmRule;
import com.service.bean.alarm.DutyAlarm;
import com.service.bean.alarm.WorkTimes;
import com.service.bean.area.Area;
import com.service.bean.area.Road;
import com.service.bean.device.Vehicle;
import com.service.bean.other.VariableData;
import com.service.bean.point.LabelPoint;
import com.service.bean.point.RoadCheckPoint;
import com.service.cache.CacheManager;
import com.service.db.DbManager;
import com.service.other.Logger;
import com.service.other.ServerConfig;
import com.service.tools.AlarmTool;

/**
 * 统计类报警
 * @author lj
 *
 */
public class StatisticsAlarm {
	/**
	 * 上班出勤统计
	 * @param vehicle
	 */
	public void workStartDutyStatistics(Vehicle vehicle) {
		if (null == vehicle.duty) {
			Logger.debug(vehicle.device.deviceNo, "车辆当前无排班");
			return;
		}
		try {
			VariableData variable = CacheManager.instance().getVariable(vehicle.device.deviceNo);
			if (null == variable.activeWorkStartTime) {
				// 在规定时间段内没有检测到上班记录, 生成出勤报表数据, 插入到数据库中
				DutyAlarm alarm = createDutyAlarm(vehicle, new Date(), 1);
				DbManager.instance().dba.saveDutyAlarm(alarm, vehicle.company);
				if (vehicle.alarmRuleMap.containsKey("123")) {
					// 如果车辆有绑定上班迟到报警, 同时生成一条报警数据
					AlarmRule rule = vehicle.alarmRuleMap.get("123");
					AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
					ai.alarmMessage = vehicle.plateNO + "在班次:" + vehicle.duty.schedName + "上班迟到";
					ai.alarmLevel = AlarmTool.getAlarmLevel(0, rule);
					CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
				} else {
					Logger.debug(vehicle.device.deviceNo, "在班次:" + vehicle.duty.schedName + "上班迟到, 没有绑定报警");
				}
			} else {
				Logger.debug(vehicle.device.deviceNo, "正常上班出勤, 出勤时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(variable.activeWorkStartTime));
			}
		} catch (Exception e) {
			Logger.err(e);
		}
		return;
	}
	
	/**
	 * 下班出勤统计
	 * @param vehicle
	 */
	public void workEndDutyStatistics(Vehicle vehicle) {
		VariableData variable = CacheManager.instance().getVariable(vehicle.device.deviceNo);
		if (null == vehicle.duty) {
			Logger.debug(vehicle.device.deviceNo, "车辆无排班");
			variable.activeWorkStartTime = null;
			variable.activeWorkEndTime = null;
			variable.workRecord = 0;
			return;
		}
		try {
			if (null == variable.activeWorkEndTime) {
				DutyAlarm alarm = null;
				String alarmMessage = null;
				if (0 == variable.workRecord) {
					alarm = createDutyAlarm(vehicle, new Date(), 0);
					alarmMessage = vehicle.plateNO + "在班次:" + vehicle.duty.schedName + "时间段内无出勤记录";
				} else {
					alarm = createDutyAlarm(vehicle, new Date(), 2);
					alarmMessage = vehicle.plateNO + "在班次:" + vehicle.duty.schedName + "下班早退";
				}
				if (null != alarm)
					DbManager.instance().dba.saveDutyAlarm(alarm, vehicle.company);
				if (vehicle.alarmRuleMap.containsKey("134")) {
					AlarmRule rule = vehicle.alarmRuleMap.get("134");
					AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
					ai.alarmMessage = alarmMessage;
					ai.alarmLevel = AlarmTool.getAlarmLevel(0, rule);
					CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
				} else {
					Logger.debug(vehicle.device.deviceNo, "在班次: " + vehicle.duty.schedName + "未出勤, 未绑定报警");
				}
			} else {
				Logger.debug(vehicle.device.deviceNo, "正常下班出勤, 出勤时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(variable.activeWorkEndTime));
			}
		} catch (Exception e) {
			Logger.err(e);
		} finally {
			variable.activeWorkStartTime = null;
			variable.activeWorkEndTime = null;
			variable.workRecord = 0;
		}
	}
	
	/**
	 * 垃圾收集点未去统计
	 * @param vehicle
	 */
	public void collectTimeStatistics(Vehicle vehicle) {
		VariableData variable = CacheManager.instance().getVariable(vehicle.device.deviceNo);
		try {
			AlarmRule rule = vehicle.alarmRuleMap.get("124");
			if (null == rule)
				return;
			if (null == variable.collPoints || 0 == variable.collPoints.size()) {
				AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
				ai.alarmMessage = vehicle.plateNO + "未去任一收集点";
				ai.alarmLevel = AlarmTool.getAlarmLevel(0L, rule);
				CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
				return;
			}
			List<LabelPoint> alarmPoints = new ArrayList<LabelPoint>();
			for (List<LabelPoint> list : vehicle.collectionPoints.values()) {
				if (null != list && 0 != list.size()) {
					for (LabelPoint point : list) {
						if (!variable.collPoints.contains(point)) {
							alarmPoints.add(point);
						}
					}
				}
			}
			if (0 != alarmPoints.size()) {
				StringBuffer alarmNames = new StringBuffer();
				for (LabelPoint p : alarmPoints) {
					alarmNames.append(p.name + ",");
				}
				String names = alarmNames.toString();
				names = names.substring(0, names.length() - 1);
				AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
				ai.alarmMessage = vehicle.plateNO + "未去垃圾收集点:" + names;
				ai.alarmLevel = AlarmTool.getAlarmLevel(0L, rule);
				CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
			}
		} catch (Exception e) {
			Logger.err(e);
		} finally {
			variable.collPoints.clear();
		}
	}
	
	/**
	 * 未按垃圾收集点顺序收集统计
	 * @param vehicle
	 */
	public void collectOrderStatistics(Vehicle vehicle) {
		if (null == vehicle.collectionPoints || 0 == vehicle.collectionPoints.size())
			// 车辆未绑定任何垃圾收集点
			return;
		VariableData variable = CacheManager.instance().getVariable(vehicle.device.deviceNo);
		try {
			if (null == variable.collectionList || 0 == variable.collectionList.size())
				return;
			int size = variable.collectionList.size();
			LabelPoint before = null;
			LabelPoint behind = null;
			boolean isNeedAlarm = false;
			for (int i = 0; i < size; i++) {
				before = variable.collectionList.get(i);
				if (i == size - 1)
					break;
				behind = variable.collectionList.get(i + 1);
				if (before.seq < behind.seq)
					continue;
				isNeedAlarm = true;
				break;
			}
			if (isNeedAlarm) {
				AlarmRule rule = vehicle.alarmRuleMap.get("131");
				AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
				ai.alarmMessage = "未按收集点顺序工作,前一个点:" + before.name + ", 后一个点: " + behind.name;
				ai.alarmLevel = AlarmTool.getAlarmLevel(0, rule);
				CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
			}
		} catch (Exception e) {
			Logger.err(e);
		} finally {
			variable.collectionList.clear();
		}
	}
	
	/**
	 * 清扫路段未去统计
	 * @param vehicle
	 */
	public void roadCleaningStatistics(Vehicle vehicle) {
		VariableData variable = CacheManager.instance().getVariable(vehicle.device.deviceNo);
		if (null == vehicle.getAreas() || 0 == vehicle.getAreas().size()) {
			Logger.debug(vehicle.device.deviceNo, "车辆未绑定任何区域");
			variable.clearWorkPointMap();
			return;
		}
		try {
			List<Road> unworkList = new ArrayList<Road>();
			for (Area area : vehicle.getAreas().values()) {
				if (null == area.roads || 0 == area.roads.size())
					continue;
				for (Road road : area.roads.values()) {
					if (null == variable.getWorkRecond(road) || 0 == variable.getWorkRecond(road).size()) {
						unworkList.add(road);
					}
				}
			}
			if (0 == unworkList.size()) {
				Logger.debug(vehicle.device.deviceNo, "没有未去的清扫路段");
			} else {
				StringBuffer buffer = new StringBuffer();
				for (Road road : unworkList) {
					buffer.append(road.roadName + ",");
				}
				String names = buffer.toString();
				names = names.substring(0, names.length() - 1);
				AlarmRule rule = vehicle.alarmRuleMap.get("125");
				AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
				ai.alarmMessage = "未去清扫路段:" + names;
				ai.alarmLevel = AlarmTool.getAlarmLevel(0, rule);
				CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
			}
		} catch (Exception e) {
			Logger.err(e);
		} finally {
			variable.clearWorkPointMap();
		}
	}
	
	/**
	 * 清扫遍数统计
	 * @param vehicle
	 */
	public void workTimesStatistics(Vehicle vehicle) {
		Logger.debug("zytj" + vehicle.device.deviceNo, "---------------------开始统计实际作业遍数---------------------");
		if(vehicle.duty == null || vehicle.getAreas() == null || vehicle.getAreas().size() == 0) {
			// 没有排班或未设置区域, 不做统计
			Logger.debug("zytj" + vehicle.device.deviceNo, "车辆无排班, 或者未绑定作业区域/路线");
			return;
		}
		VariableData variable = CacheManager.instance().getVariable(vehicle.device.deviceNo);
		try {
			// 一条路段对应一个集合, 集合用于排序路段中每个检测点的经过回数(经过一回记为清扫一回)
			List<Integer> times = null;
			// 用于保存需要报警的路段
			List<String> alarmRoads = new ArrayList<String>();
			// 用于报表统计
			List<WorkTimes> workTimesList = new ArrayList<WorkTimes>();
			for (Area area : vehicle.getAreas().values()) {
				if (null == area.roads || 0 == area.roads.size()) {
					Logger.debug("zytj" + vehicle.device.deviceNo, "车辆绑定的区域/路线: " + area.name + "未绑定路段, 检测下一个区域/路线");
					continue;
				}
				for (Road road : area.roads.values()) {
					if (null == road.checkPoints || 0 == road.checkPoints.size()) {
						Logger.debug("zytj" + vehicle.device.deviceNo, "车辆绑定的路段: " + road.roadName + "未绑定路段检测点, 检测下一个路段");
						continue;
					}
					times = new ArrayList<Integer>();
					Logger.debug("zytj" + vehicle.device.deviceNo, "+++++++++++++++++++++" + road.roadName + " start+++++++++++++++++++++");
					for (Entry<String, Integer> entry : variable.workTimesMap.entrySet()) {
						Logger.debug("zytj" + vehicle.device.deviceNo, entry.getKey() + ", " + entry.getValue());
					}
					Logger.debug("zytj" + vehicle.device.deviceNo, "+++++++++++++++++++++" + road.roadName + " end+++++++++++++++++++++");
					for (RoadCheckPoint point : road.checkPoints.values()) {
						if (variable.workTimesMap.containsKey(point.checkPointId)) {
							int i = variable.workTimesMap.get(point.checkPointId);
							times.add(i);
						}
					}
					if (times.size() < road.checkPoints.size()) {
						StringBuffer b = new StringBuffer();
						for (String pointId : variable.workTimesMap.keySet()) {
							if (!road.checkPoints.containsKey(pointId)) {
								b.append(pointId + ",");
							}
						}
						Logger.debug("zytj" + vehicle.device.deviceNo, "车辆有以下路段检测点未去: " + b.toString());
						alarmRoads.add(road.roadName);
						continue;
					}
					// 对路段中每个检测点的实际清扫次数进行从小到大的排序
					Collections.sort(times, new Comparator<Integer>() {
						public int compare(Integer o1, Integer o2){
							return o1.compareTo(o2);
						}
					});
					if (null == times || 0 == times.size())
						continue;
					if (road.clearWays > 1) {
						if (road.clearWays == 2) {
							// 假设一条路段需要清扫2回, 因为转弯点的实际清扫次数会少一次, 所以在最少的那个点的实际清扫次数+1
							int x = times.get(0);
							times.remove(0);
							times.add(x + 1);
						} else {
							for (int i = 0; i < road.clearWays - 1; i++) {
								int x = times.get(i);
								times.remove(i);
								times.add(x + 1);
							}
						}
					}
					// 重新对清扫次数的集合进行从小到大的排序
					Collections.sort(times, new Comparator<Integer>() {
						public int compare(Integer o1, Integer o2) {
							return o1.compareTo(o2);
						}
					});
					// 假设共10个检测点, 配置文件中的作业回数百分比为80, 则只取10个检测点中实际作业回数最多的8个点
					if (road.checkPoints.size() > 2) {
						// 总作业回数
						int totalWays = road.clearTimes * road.clearWays;
						// 按百分比截取后的总作业回数
						int activeWays = totalWays * ServerConfig.instance().workTimesPercent / 100;
						if (activeWays == 0)
							// 至少保留一个作业检测点
							activeWays = 1;
						if (totalWays > activeWays) {
							for (int i = 0; i < (totalWays - activeWays); i++) {
								times.remove(0);
							}
						}
					}
					WorkTimes wt = createWorkTimes(vehicle, area, road, road.clearTimes * road.clearWays, times.get(0));
					if (null != wt) 
						workTimesList.add(wt);
					if (times.get(0) < road.clearTimes * road.clearWays) {
						// 集合中数字最小的那个点表示实际清扫次数, 如果实际的清扫次数小于规定作业次数, 将需要报警的路段放到集合中,遍历完所有区域后报警
						Logger.debug("zytj" + vehicle.device.deviceNo, "作业次数未达到,路段" + road.roadName 
								+ "--规定作业总回数:" + road.clearTimes * road.clearWays + ", 实际作业总回数:" + times.get(0));
						alarmRoads.add(road.roadName);
					}
				}
			}
			if (alarmRoads.size() > 0) {
				StringBuffer buffer = new StringBuffer();
				for (String s : alarmRoads) {
					buffer.append(s + ",");
				}
				String names = alarmRoads.toString();
				names = names.substring(0, names.length() - 1);
				AlarmRule rule = vehicle.alarmRuleMap.get("126");
                AlarmInfo ai = AlarmTool.createAlarmInfo(vehicle, rule);
                ai.alarmMessage = "以下路段作业次数未达到:" + names;
                ai.alarmLevel = AlarmTool.getAlarmLevel(0, rule);
                CacheManager.instance().getAlarmSaveQueue(vehicle.company).offer(ai);
			}
			if (null != workTimesList && 0 != workTimesList.size()) {
				DbManager.instance().dba.batchSaveWorkTimes(workTimesList, vehicle.company);
			}
		} catch (Exception e) {
			Logger.err(e);
		} finally {
			variable.workTimesMap.clear();
			variable.passCheckPoints.clear();
		}
	}
	
	/**
	 * 生成实际作业遍数对象, 给报表使用
	 * @param vehicle
	 * @param area
	 * @param road
	 * @param plan
	 * @param actual
	 * @return
	 */
	private WorkTimes createWorkTimes(Vehicle vehicle, Area area, Road road, int plan, int actual) {
		WorkTimes wt = new WorkTimes();
		try {
			wt.equipmentId = vehicle.equipmentId;
			wt.areaId = area.id;
			wt.roadId = road.roadId;
			wt.planJobTime = plan;
			wt.actualJobTime = actual;
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			wt.reportDate = sdf.parse(sdf.format(new Date()));
		} catch (Exception e) {
			Logger.err(e);
			wt = null;
		}
		return wt;
	}
	
	/**
	 * 生成排班报警信息
	 * @param vehicle
	 * @param alarmTime
	 * @param status 0:缺勤</br>1:迟到</br>2:早退
	 * @return
	 */
	private DutyAlarm createDutyAlarm(Vehicle vehicle, Date alarmTime, int status) {
		DutyAlarm alarm = new DutyAlarm();
		alarm.equipmentId = vehicle.equipmentId;
		alarm.schedId = vehicle.duty.schedId;
		alarm.alarmDate = alarmTime;
		alarm.attendanceStatus = status;
		return alarm;
	}
	
}
